home *** CD-ROM | disk | FTP | other *** search
- /* DriverPrepRequest.c */
- /*
- * DriverPrepRequest.c
- * Copyright © 1994-95 Apple Computer Inc. All rights reserved.
- */
- /* .___________________________________________________________________________________.
- | These routines manage the details of calling PrepareMemoryForIO for a PBRead or |
- | or PBWrite request. The process is normally straightforward, but can become |
- | quite complex. The code flow for simple transfers is as follows: |
- | 1. PBRead/PBWrite calls PrepareNewDMATransfer |
- | 2. PrepareNewDMATransfer calls PrepareMemoryForIO and PrepareNextDMA |
- | 3. On return, the DMA parameters are sent to the hardware. |
- | The more complex code paths handle the following problems: |
- | If the user area is discontiguous, PrepareNextDMA will be re-called from the |
- | Primary Interrupt Service Routine to compute the next physical transfer |
- | area and length. In many drivers this would be pre-processed into a |
- | device-specific scatter-gather list. |
- | If PrepareMemoryForIO did not prepare the entire area, it must be re-called |
- | to handle partial preparation. In this case, the primary interrupt service |
- | routine queues a Software Task and exits the interrupt without restarting |
- | the device. When the Software Task runs, it calls PrepareMemoryForIO, |
- | re-calls PrepareNextDMA, and queues a Secondary Interrupt handler that |
- | restarts I/O. Error handling in this case is tricky, as an error must |
- | cause the device to abort the transfer (otherwise subsequent transfers |
- | won't be started. |
- | One further complication: if the hardware has a maximum transfer length, the |
- | preparation might need to be re-started from the beginning (after updating |
- | the callers buffer pointers and transfer length). This is handled by the |
- | Software Task. |
- | Functions: |
- | PrepareNewDMATransfer Called from driver mainline to initialize variables and |
- | prepare the first DMA sequence. |
- | PrepareNextDMATask Software Task queued from the primary interrupt to |
- | continue preparation. |
- | PrepareNextDMA Called after preparation and I/O to compute the next |
- | DMA physical address and transfer length. |
- | ConfigureThisPhysicalArea Performs the actual address/length computation. |
- | PrepareNextDMA returns a status value that controls the caller: |
- | noErr Success: start (or restart) I/O |
- | kPrepareMemoryRestart After a primary interrupt: the function queued a Software |
- | Task to handle partial preparation. The interrupt routine |
- | should exit without restarting I/O. When the Software Task |
- | completes, it will queue our Secondary Interrupt handler |
- | with this status to continue the I/O operation. |
- | scsiDataRunError Failure: The I/O table "done" state was set. This means the |
- | device wants to transmit more data than the application. |
- | The interrupt routine should halt the transfer. |
- | other error Failure from PrepareMemoryForIO. The interrupt routine |
- | should halt the transfer. |
- | The Software Task queues the common Secondary Interrupt Handler with status set |
- | as follows: |
- | kPrepareMemoryRestart Success after partial preparation: restart I/O |
- | If there is an error, the SCSI Script will eventually |
- | fail through its I/O rundown and return scsiDataRunError |
- | (The only error returned by PrepareMemoryForIO is paramErr |
- | which isn't too useful.) |
- .___________________________________________________________________________________.
- */
- #include "NCRDriverPrivate.h"
- /*
- * The current request will always be referenced through this local variable.
- */
- #define REQUEST (*perRequestDataPtr)
- #define PB (*((IOParam *) REQUEST.pb))
- #define SCSI (*((NCRSCSIParamPtr) PB.ioMisc))
- #define IOTABLE (REQUEST.scsiIOTable)
-
- OSErr DoInitialPreparation(
- PerRequestDataPtr perRequestDataPtr
- );
-
- /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
- * PrepareNewDMATransfer initializes a user I/O data transfer request. It initializes
- * the dma parameters, calls PrepareMemoryForIO, and specifies the first DMA address
- * and length. If this transfer does not support a data phase, it returns noErr without
- * preparing anything.
- */
- OSErr
- PrepareNewDMATransfer(
- PerRequestDataPtr perRequestDataPtr
- )
- {
- OSErr status;
- IOPreparationOptions options;
-
- Trace(PrepareNewDMATransfer);
- CLEAR(IOTABLE);
- IOTABLE.preparationID = kInvalidID; /* Marker for CheckpointIOTable */
- REQUEST.dmaFirstPrepared = 0;
- REQUEST.dmaLengthPrepared = 0;
- REQUEST.userBufferStart = NULL;
- REQUEST.userBufferEnd = NULL;
- switch (SCSI.driverAction) {
- case kNCRDriverNoDataPhase:
- status = noErr;
- options = 0;
- break;
- case kNCRDriverInputAllowed:
- options = kIOIsInput;
- break;
- case kNCRDriverOutputAllowed:
- options = kIOIsOutput;
- break;
- default:
- options = 0;
- status = paramErr;
- break;
- }
- if (options != 0) { /* Preparation required */
- REQUEST.userBufferStart = PB.ioBuffer;
- REQUEST.amountToTransfer = PB.ioReqCount;
- REQUEST.userBufferEnd = PB.ioBuffer + PB.ioReqCount;
- PB.ioActCount = 0;
- IOTABLE.options =
- ( options /* Input or output */
- | (0 * kIOMultipleRanges) /* No scatter-gather list */
- | (1 * kIOLogicalRanges) /* Logical addresses */
- | (0 * kIOMinimalLogicalMapping) /* Normal logical mapping */
- | (1 * kIOShareMappingTables) /* Share with Kernel */
- | (0 * kIOCoherentDataPath) /* No fancy data path */
- );
- IOTABLE.addressSpace = REQUEST.addressSpaceID;
- IOTABLE.logicalMapping = NULL;
- /*
- * This is called from DoDriverIO or a Software Task to start a new
- * I/O preparation. Note: we do not come here for partial preparation.
- */
- status = DoInitialPreparation(perRequestDataPtr);
- }
- return (status);
- }
-
- /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
- * PrepareNextDMATask is the SoftwareTask that is executed in order to start the next
- * PrepareMemoryForIO request. Error handling needs to be improved.
- */
- void
- PrepareNextDMATask(
- void *p1, /* perRequestDataPtr */
- void *p2 /* Unused */
- )
- {
- OSErr status;
- #define perRequestDataPtr ((PerRequestDataPtr) p1)
-
- LogString("\pPrepareNextDMATask - Software Task");
- Trace(PrepareNextDMATask);
- UNUSED(p2);
- if ((IOTABLE.state & kIOStateDone) != 0) {
- /*
- * We need to start again (with the rest of the user buffer).
- */
- LogString("\pPrepareNextDMATask: restart");
- status = DoInitialPreparation(perRequestDataPtr);
- }
- else {
- /*
- * Partial preparation.
- */
- LogDecimal(IOTABLE.firstPrepared, "\pfirstPrep: NextDMA calls PrepareMemoryForIO");
- status = PrepareMemoryForIO(&REQUEST.scsiIOTable);
- CheckStatus(status, "\pPrepareMemoryForIO partial prep");
- if (status == noErr)
- status = PrepareNextDMA(perRequestDataPtr);
- }
- /*
- * Restart I/O - we do this by calling our common Secondary Interrupt routine.
- * The status value tells the Secondary Interrupt routine to restart the device..
- */
- LogString("\pPrepareNextDMATask queues SIH");
- (void) NCRQueueSecondaryInterrupt(perRequestDataPtr, kPrepareMemoryRestart);
- #undef perRequestDataPtr
- }
-
-
- /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
- * DoInitialPreparation is called from the driver mainline to start a transfer, and
- * from the Software Task to start a new preparation after updating the userBuffer
- * pointers.
- */
- OSErr
- DoInitialPreparation(
- PerRequestDataPtr perRequestDataPtr
- )
- {
- OSErr status;
-
- Trace(DoInitialPreparation);
- if (REQUEST.userBufferStart >= REQUEST.userBufferEnd) {
- /*
- * The device has/needs more data than the caller supplied.
- */
- status = scsiDataRunError;
- }
- else {
- IOTABLE.rangeInfo.range.base = REQUEST.userBufferStart;
- IOTABLE.rangeInfo.range.length = REQUEST.amountToTransfer;
- if (IOTABLE.rangeInfo.range.length > kNCRDriverMaxTransfer)
- IOTABLE.rangeInfo.range.length = kNCRDriverMaxTransfer;
- REQUEST.amountToTransfer -= IOTABLE.rangeInfo.range.length;
- (* (UInt32 *) &REQUEST.userBufferStart) +=
- (UInt32) IOTABLE.rangeInfo.range.length;
- IOTABLE.mappingEntryCount = GetMapEntryCount(
- IOTABLE.rangeInfo.range.base,
- IOTABLE.rangeInfo.range.length
- );
- if (IOTABLE.mappingEntryCount > REQUEST.scsiMapEntries)
- IOTABLE.mappingEntryCount = REQUEST.scsiMapEntries;
- IOTABLE.physicalMapping = REQUEST.physicalMapTables;
- IOTABLE.state = 0; /* Clear done bit */
- IOTABLE.firstPrepared = 0;
- status = PrepareMemoryForIO(&IOTABLE);
- CheckStatus(status, "\pPrepareMemoryForIO initial");
- if (status == noErr && IOTABLE.lengthPrepared == 0)
- status = scsiDataRunError;
- }
- if (status == noErr) {
- REQUEST.dmaFirstPrepared = 0;
- REQUEST.dmaLengthPrepared = 0;
- status = PrepareNextDMA(perRequestDataPtr);
- }
- return (status);
- }
-
- /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
- * PrepareNextDMA handles the details of a single DMA burst. Here, we assume that we
- * cannot build a physical scatter-gather table, but must construct each DMA transfer
- * by itself. It uses the following from the REQUEST record:
- * REQUEST.scsiIOTable This will be prepared by the initial call
- * and used by other calls.
- * REQUEST.amountToTransfer The number of bytes remaining in the user
- * ioReqCount.
- * REQUEST.userBufferStart The start of the user buffer -- updated when
- * we call PrepareMemoryForIO for a new preparation.
- * REQUEST.userBufferEnd The permanent end of the user buffer.
- * REQUEST.dmaFirstPrepared Offset to already prepared data. This is always
- * >= perRequestIOTable.firstPrepared.
- * REQUEST.dmaLengthPrepared Amount already prepared. This is always
- * <= perRequestIOTable.lengthPrepared.
- * REQUEST.physicalMapIndex Where to start the next I/O (output only).
- * Return:
- * noErr Success -- start or continue the transfer.
- * scsiDataRunError Failure - no more data can be transfered.
- * other errors Failure.
- * Return scsiDataRunError if we're at the end of the transfer, or noErr if we
- * did the preparation. The Primary Interrupt routine will send a Software Interrupt,
- * other states will fail.
- */
- OSErr
- PrepareNextDMA(
- PerRequestDataPtr perRequestDataPtr
- )
- {
- OSErr status;
- ItemCount index;
-
- Trace(PrepareNextDMA);
- if (REQUEST.dmaFirstPrepared >= IOTABLE.lengthPrepared) {
- /*
- * We are off the end of the preparation. The caller must check the
- * caller's ioReqCount to see if this terminates I/O. If not, the caller
- * will, eventually, use a Software Interrupt to call PrepareMemoryForIO.
- * When that happens, IOTABLE.firstPrepared will have been advanced to
- * continue the preparation.
- */
- IOTABLE.firstPrepared += IOTABLE.lengthPrepared;
- REQUEST.dmaLengthPrepared = 0;
- status = scsiDataRunError;
- //** LogString("\pOff the end of the preparation");
- }
- else {
- status = noErr;
- REQUEST.dmaLengthPrepared = GLOBAL.pageSize;
- if (REQUEST.dmaFirstPrepared < GLOBAL.pageSize) {
- /*
- * This is the first preparation. It starts at the first physical
- * page entry (which is not necessarily page aligned) and extends
- * -- at most -- to the end of the page.
- */
- REQUEST.physicalMapIndex = 0;
- REQUEST.dmaLengthPrepared -=
- ((UInt32) IOTABLE.physicalMapping[0] & GLOBAL.pageMask);
- }
- else {
- /*
- * We are in the middle of a DMAsequence. The transfer length is a
- * page size and we start at the current preparation.
- */
- REQUEST.physicalMapIndex =
- ((REQUEST.dmaFirstPrepared - 1) / GLOBAL.pageSize) + 1;
- }
- /*
- * We know where to start the transfer. Compute the number of bytes to
- * transfer on this DMA cycle, extending the transfer as long as there
- * are contiguous physical pages.
- */
- index = REQUEST.physicalMapIndex;
- while (REQUEST.dmaLengthPrepared < IOTABLE.lengthPrepared
- && NextPageIsContiguous(&IOTABLE, index)) {
- REQUEST.dmaLengthPrepared += GLOBAL.pageSize;
- index++;
- }
- if (REQUEST.dmaLengthPrepared > IOTABLE.lengthPrepared)
- REQUEST.dmaLengthPrepared = IOTABLE.lengthPrepared;
- }
- #if 0 && USE_LOG_LIBRARY
- WriteLogEntry(GLOBAL.logRecordPtr, 'pDMA',
- LogFormat5(kLogFormatSigned, kLogFormatUnsigned, kLogFormatUnsigned,
- kLogFormatAddress, kLogFormatString),
- (signed long) status,
- REQUEST.dmaFirstPrepared,
- REQUEST.dmaLengthPrepared,
- IOTABLE.physicalMapping[REQUEST.physicalMapIndex],
- "\psts 1st len add"
- );
- #endif
- return (status);
- }
-